import { Injectable } from '@angular/core';
import { AbstractControl, FormBuilder, FormGroup, Validators } from '@angular/forms';
import {
  ArrayFieldSchemaSection,
  BooleanFieldSchemaSection,
  FieldSchemaSection,
  IntegerFieldSchemaSection,
  NumberFieldSchemaSection,
  ObjectFieldSchemaSection,
  StringFieldSchemaSection
} from './json-schema';

@Injectable({
  providedIn: 'root'
})
export class FormBuilderService {

  constructor(private builder: FormBuilder) { }

  public buildForm(schema: ObjectFieldSchemaSection): FormGroup {
    const group = this.buildControlGroup("", schema);
    return group;
  }

  private buildStringControl(name: string, isRequired: boolean, schema: StringFieldSchemaSection): AbstractControl {
    const { minLength, maxLength, title, pattern, format, } = schema;

    const validators = [];
    if (isRequired) {
      validators.push(Validators.required);
    }
    if (pattern) {
      validators.push(Validators.pattern(pattern));
    }
    if (minLength) {
      validators.push(Validators.minLength(minLength));
    }
    if (maxLength) {
      validators.push(Validators.maxLength(maxLength));
    }
    // todo:
    // if(format){
    //   validators.push(Validators.pattern(format));
    // }
    const control = this.builder.control(name, validators);
    return control;
  }

  private buildIntegerControl(name: string, isRequired: boolean, schema: IntegerFieldSchemaSection): AbstractControl {
    const { minimum, maximum } = schema;
    const validators = [];
    if (isRequired) {
      validators.push(Validators.required);
    }
    if (typeof minimum == "number") {
      validators.push(Validators.min(minimum));
    } if (typeof maximum == "number") {
      console.log(maximum);
      validators.push(Validators.max(maximum));
    }
    const control = this.builder.control(name, validators);
    return control;
  }

  private buildNumberControl(name: string, isRequired: boolean, schema: NumberFieldSchemaSection): AbstractControl {
    const { minimum, maximum } = schema;
    const validators = [];
    if (isRequired) {
      validators.push(Validators.required);
    }
    if (typeof minimum == "number") {
      validators.push(Validators.min(minimum));
    } if (typeof maximum == "number") {
      console.log(maximum);
      validators.push(Validators.max(maximum));
    }

    const control = this.builder.control(name, validators);
    return control;
  }

  private buildBooleanControl(name: string, isRequired: boolean, schema: BooleanFieldSchemaSection): AbstractControl {
    const validators = [];
    if (isRequired) {
      validators.push(Validators.required);
    }
    const control = this.builder.control(name, validators);
    return control;
  }
  private buildArrayControl(name: string, isRequired: boolean, schema: ArrayFieldSchemaSection): AbstractControl {
    const { items } = schema;
    if (items instanceof Array) {
      const arr = <FieldSchemaSection[]>items;
    }

    return this.builder.array([]);
  }

  private buildControlGroup(name: string, schema: ObjectFieldSchemaSection) {
    const group = this.builder.group({});
    const { properties, required } = schema;

    for (const key in properties) {
      const isRequired: boolean = required?.find(x => x == key) ? true : false;
      if (Object.prototype.hasOwnProperty.call(properties, key)) {
        const element = properties[key];
        switch (element.type) {
          case "string":
            group.addControl(key, this.buildStringControl(key, isRequired, element));
            break;
          case "integer":
            group.addControl(key, this.buildIntegerControl(key, isRequired, element));
            break;
          case "number":
            group.addControl(key, this.buildNumberControl(key, isRequired, element));
            break;
          case "boolean":
            group.addControl(key, this.buildBooleanControl(key, isRequired, element));
            break;
          case "array":
            group.addControl(key, this.buildArrayControl(key, isRequired, element));
            break;
          // case "enum":
          //   break;
          case "object":
            group.addControl(key, this.buildControlGroup(key, element));
            break;
        }
      }
    }
    return group;
  }
}
